package com.feicheng.wxapi.app;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.feicheng.common.constant.Constant;
import com.feicheng.common.exception.SystemException;
import com.feicheng.wxapi.app.token.AccessToken;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;

@PropertySource(value = {"classpath:wxapi.properties"}, encoding = "UTF-8")
@Component("wx-api-app")
@Data
@EqualsAndHashCode(callSuper = false)
@Slf4j
public class WxApi {
	
	@Value("${getAccessTokenUrl}")
	private String getAccessTokenUrl;
	
	@Value("${code2Session}")
	private String code2SessionUrl;
	
	@Value("${getPhoneNumber}")
	private String getPhoneNumberUrl;
	
	@Autowired
    RestTemplate restTemplate;

	private Map<String, AccessToken> accessTokenMap = new HashMap<String, AccessToken>();
	
	private AccessToken newAccessToken(String appId, String secret) {
		
		StringBuilder sbUrlAccessToken = new StringBuilder();
        sbUrlAccessToken.append(getAccessTokenUrl)
        		.append(Constant.HTTP.REQUEST.QU)
                .append(Constant.WX.PARAM.APPID).append(Constant.HTTP.REQUEST.EQ).append(appId)
                .append(Constant.HTTP.REQUEST.AD).append(Constant.WX.PARAM.SECRET).append(Constant.HTTP.REQUEST.EQ).append(secret)
                .append(Constant.HTTP.REQUEST.AD).append(Constant.WX.PARAM.GRANT_TYPE).append(Constant.HTTP.REQUEST.EQ).append("client_credential");

        ResponseEntity<String> resultAccessToken = restTemplate.getForEntity(sbUrlAccessToken.toString(), String.class);

        JSONObject resAccessToken = JSONObject.parseObject(resultAccessToken.getBody());

        String token = resAccessToken.getString(Constant.WX.RESPONSE.ACCESS_TOKEN);
        if (!StringUtils.hasLength(token)) {
            throw new SystemException("取得调用凭据失败");
        }
        
        
        Integer expiresIn = resAccessToken.getInteger(Constant.WX.RESPONSE.EXPIRES_IN);
        
        AccessToken accessToken = new AccessToken();
        
        accessToken.setAccessToken(token);
        
        LocalDateTime tag = LocalDateTime.now();
        tag = tag.plusSeconds(expiresIn - 10);
        
        accessToken.setGetTime(tag);
        
		return accessToken;
		
	}

	private String getAccessToken(String appId, String secret) {

		AccessToken accessToken = accessTokenMap.get(appId);

		if (null != accessToken) {

			LocalDateTime current = LocalDateTime.now();

			if (current.isAfter(accessToken.getGetTime())) {
				synchronized (appId.intern()) {
					accessToken = this.newAccessToken(appId, secret);
					accessTokenMap.put(appId, accessToken);
				}
			}
		} else {
			synchronized (appId.intern()) {
				if (null == accessToken) {
					accessToken = this.newAccessToken(appId, secret);
					accessTokenMap.put(appId, accessToken);
				}
			}
		}

		return accessToken.getAccessToken();
	}
	
	public String getPhoneNumber(String openId, String appId, String secret, String code) {

		StringBuilder sbUrl = new StringBuilder();
        sbUrl.append(getPhoneNumberUrl)
        .append(Constant.HTTP.REQUEST.QU)
        .append(Constant.WX.PARAM.ACCESS_TOKEN)
				.append(Constant.HTTP.REQUEST.EQ)
				.append(this.getAccessToken(appId, secret));
        
        HttpHeaders headers = new HttpHeaders();

        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");

        headers.setContentType(type);

        headers.add("Accept", MediaType.APPLICATION_JSON.toString());

        JSONObject param = new JSONObject();     
        param.put(Constant.WX.PARAM.CODE, code);
        
        HttpEntity<String> formEntity = new HttpEntity<String>(param.toJSONString(), headers);
        
        ResponseEntity<String> result = restTemplate.postForEntity(sbUrl.toString(), formEntity, String.class);
        
        
        JSONObject jo = JSON.parseObject(result.getBody());
        
        
        Integer errCode = jo.getInteger(Constant.WX.RESPONSE.ERRCODE);
        
        if (null == errCode || errCode != 0) {
        	throw new SystemException(jo.getString(Constant.WX.RESPONSE.ERRMSG));
        }

        if (jo.containsKey(Constant.WX.RESPONSE.PHONE_INFO)) {
        	return jo.getJSONObject(Constant.WX.RESPONSE.PHONE_INFO).getString(Constant.WX.RESPONSE.PURE_PHONE_NUMBER);
        }
		
		throw new SystemException("取得电话号失败");
	}
	

	
	public String getOpenId(String code, String appId, String secret) {
		try {
			StringBuilder sbUrl = new StringBuilder();
	        sbUrl.append(code2SessionUrl)
	        		.append(Constant.HTTP.REQUEST.QU)
	                .append(Constant.WX.PARAM.APPID).append(Constant.HTTP.REQUEST.EQ).append(appId)
	                .append(Constant.HTTP.REQUEST.AD).append(Constant.WX.PARAM.SECRET).append(Constant.HTTP.REQUEST.EQ).append(secret)
	                .append(Constant.HTTP.REQUEST.AD)
	                .append(Constant.WX.PARAM.GRANT_TYPE)
	                .append(Constant.HTTP.REQUEST.EQ)
	                .append(Constant.WX.PARAM.AUTHORIZATION_CODE)
	                .append(Constant.HTTP.REQUEST.AD)
	                .append(Constant.WX.PARAM.JS_CODE)
	                .append(Constant.HTTP.REQUEST.EQ).append(code);
	        ResponseEntity<String> result = restTemplate.getForEntity(sbUrl.toString(), String.class);

	        JSONObject jo = JSON.parseObject(result.getBody());
	        if (jo.containsKey(Constant.WX.RESPONSE.OPENID)) {
	            return (String) jo.get(Constant.WX.RESPONSE.OPENID);
	        }
		} catch(Exception e) {
			log.error("取得openId失败，原因：", e);
		}
		
		throw new SystemException("取得openId失败");
	}
}
